/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package App;

import static App.RandomMoveUtils.calcPlayerPosition;
import generated.AwaitMoveMessageType;
import generated.BoardType;
import generated.CardType;
import generated.MazeCom;
import generated.MazeComType;
import generated.ObjectFactory;
import generated.PositionType;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import networking.MazeComMessageFactory;
import utils.BoardTypeAnalyser;

/**
 *
 * @author lars.kloeser
 */
public class MoveFactory extends MazeComMessageFactory {

    private ObjectFactory of = new ObjectFactory();
    private ReturnObjekt topMove = null;
    private ReturnObjekt secMove = null;
    private int id;
    private AwaitMoveMessageType await;
    private ExecutorService threadPool;
    public static BlockingQueue<ReturnObjekt> moves;

    public MoveFactory() {
        this.threadPool = Executors.newFixedThreadPool(48);
        moves = new ArrayBlockingQueue(48);
    }

    public void calcMoves(int newID, AwaitMoveMessageType await) throws InterruptedException {
        this.id = newID;
        this.await = await;
        moves.clear();
        BoardType board = await.getBoard();
        PositionType forbidden = board.getForbidden();
        if (null == forbidden) {
            forbidden = new PositionType();
            forbidden.setCol(0);
            forbidden.setRow(0);
        }
        CardType card = await.getBoard().getShiftCard();
        boolean[] openings = {card.getOpenings().isTop(), card.getOpenings().isRight(), card.getOpenings().isBottom(), card.getOpenings().isLeft()};
        this.calc0Move(card, forbidden, openings);
        this.calc6Move(card, forbidden, openings);
        int m = 44;
        if (forbidden.getCol() == 0 && forbidden.getRow() == 0) {
            m = 48;
        }
        for (int i = 0; i < m; i++) {
            ReturnObjekt obj = moves.take();
            if (null == topMove) {
                topMove = obj;
                continue;
            }
            if (obj.getEvaluation() < topMove.getEvaluation()) {
                secMove = topMove;
                topMove = obj;
            } else {
                if (null == secMove) {
                    secMove = obj;
                    continue;
                }
                if (obj.getEvaluation() < secMove.getEvaluation()) {
                    secMove = obj;
                }
            }
        }
    }

    private void calc0Move(CardType card, PositionType forbidden, boolean[] openings) {
        for (int c = 1; c <= 5; c += 2) {
            int col = c;
            int row = 0;
            PositionType movePosition = new PositionType();
            movePosition.setCol(col);
            movePosition.setRow(row);
            CardType moveCard = card;
            if (!(movePosition.getCol() == forbidden.getCol() && movePosition.getRow() == forbidden.getRow())) {
                for (int s = 0; s < 4; s++) {
                    CardType.Openings open = new CardType.Openings();
                    open.setTop(openings[s % 4]);
                    open.setRight(openings[(s + 1) % 4]);
                    open.setBottom(openings[(s + 2) % 4]);
                    open.setLeft(openings[(s + 3) % 4]);
                    moveCard.setOpenings(open);
                    Runnable calc = new CalculationForThread(id, movePosition, BoardTypeAnalyser.findPlayer(id, await.getBoard()), await.getBoard(), moveCard, await.getTreasure());
                    calc.run();
                }
            }
            int temp = col;
            col = row;
            row = temp;
            movePosition.setCol(col);
            movePosition.setRow(row);
            if (!(movePosition.getCol() == forbidden.getCol() && movePosition.getRow() == forbidden.getRow())) {
                for (int s = 0; s < 4; s++) {
                    CardType.Openings open = new CardType.Openings();
                    open.setTop(openings[s % 4]);
                    open.setRight(openings[(s + 1) % 4]);
                    open.setBottom(openings[(s + 2) % 4]);
                    open.setLeft(openings[(s + 3) % 4]);
                    moveCard.setOpenings(open);
                    Runnable calc = new CalculationForThread(id, movePosition, BoardTypeAnalyser.findPlayer(id, await.getBoard()), await.getBoard(), moveCard, await.getTreasure());
                    calc.run();
                }
            }
        }
    }

    private void calc6Move(CardType card, PositionType forbidden, boolean[] openings) {
        for (int c = 1; c <= 5; c += 2) {
            int col = c;
            int row = 6;
            PositionType movePosition = new PositionType();
            movePosition.setCol(col);
            movePosition.setRow(row);
            CardType moveCard = card;
            if (!(movePosition.getCol() == forbidden.getCol() && movePosition.getRow() == forbidden.getRow())) {
                for (int s = 0; s < 4; s++) {
                    CardType.Openings open = new CardType.Openings();
                    open.setTop(openings[s % 4]);
                    open.setRight(openings[(s + 1) % 4]);
                    open.setBottom(openings[(s + 2) % 4]);
                    open.setLeft(openings[(s + 3) % 4]);
                    moveCard.setOpenings(open);
                    Runnable calc = new CalculationForThread(id, movePosition, BoardTypeAnalyser.findPlayer(id, await.getBoard()), await.getBoard(), moveCard, await.getTreasure());
                    calc.run();
                }
            }
            int temp = col;
            col = row;
            row = temp;
            movePosition.setCol(col);
            movePosition.setRow(row);
            if (!(movePosition.getCol() == forbidden.getCol() && movePosition.getRow() == forbidden.getRow())) {
                for (int s = 0; s < 4; s++) {
                    CardType.Openings open = new CardType.Openings();
                    open.setTop(openings[s % 4]);
                    open.setRight(openings[(s + 1) % 4]);
                    open.setBottom(openings[(s + 2) % 4]);
                    open.setLeft(openings[(s + 3) % 4]);
                    moveCard.setOpenings(open);
                    Runnable calc = new CalculationForThread(id, movePosition, BoardTypeAnalyser.findPlayer(id, await.getBoard()), await.getBoard(), moveCard, await.getTreasure());
                    calc.run();
                }
            }
        }
    }

    public MazeCom getTopMove() {
        return topMove.getMazeCom();
    }
    
    public MazeCom getDummyMove(int newID, AwaitMoveMessageType await) {
        //Setup MazeCom
        MazeCom mc = of.createMazeCom();
        mc.setMcType(MazeComType.MOVE);
        mc.setId(id);
        mc.setMoveMessage(of.createMoveMessageType());
        BoardType board = await.getBoard();
        CardType card = await.getBoard().getShiftCard();

        // calc card position
        PositionType pos = new PositionType();

        pos.setCol(0);
        pos.setRow(1);

        if (board.getForbidden() != null && board.getForbidden().equals(pos)) {
            pos.setRow(3);
        }
        //set shift Card
        mc.getMoveMessage().setShiftPosition(pos);
        mc.getMoveMessage().setShiftCard(card);

        //calc Player Position
        PositionType posPlayer = BoardTypeAnalyser.findPlayer(id, board);
        mc.getMoveMessage().setNewPinPos(posPlayer);
        return mc;
    }

    public MazeCom getAlterativeMove() {
        return secMove.getMazeCom();
    }

    public MazeCom getRandomMove(int newID, AwaitMoveMessageType await) {
        BoardType board = await.getBoard();
        PositionType shiftCardPos = RandomMoveUtils.calcShiftCard(board.getForbidden());
        System.out.println("shift Card col = " + shiftCardPos.getCol() + " row = " + shiftCardPos.getRow());
        PositionType playerPos = BoardTypeAnalyser.findPlayer(newID, board);
        PositionType playerPosition = calcPlayerPosition(playerPos, board);
        System.out.println("player Card col = " + playerPosition.getCol() + " row = " + playerPosition.getRow());
        MazeCom mc = of.createMazeCom();
        mc.setMcType(MazeComType.MOVE);
        mc.setId(id);

        mc.setMoveMessage(of.createMoveMessageType());
        mc.getMoveMessage().setNewPinPos(playerPosition);
        mc.getMoveMessage().setShiftPosition(shiftCardPos);
        mc.getMoveMessage().setShiftCard(board.getShiftCard());
        return mc;
    }
}
